(() => {
  /* 
类的基本定义与使用
*/

class Person {
  // 声明属性(必须写)
  name: string;
  age: number;

  // 构造方法
  constructor (name: string, age: number) {
    this.name = name
    this.age = age
  }

  // 一般方法
  sayInfo (): void {
    console.log(`我叫${this.name}, 今年${this.age}`)
  }

  test = () => {

  }
}
// 创建类的实例
const p = new Person('tom', 12)
// 调用实例的方法
p.sayInfo()
console.log(p)

})();

(() => {
  /* 
  类的继承
  */

  class Animal {
    run (distance: number) {
      console.log(`Animal run ${distance}m`)
    }
  }

  class Dog extends Animal {
    cry () {
      console.log('wang! wang!')
    }
  }

  const dog = new Dog()
  dog.cry() 
  dog.run(100) // 可以调用从父中继承得到的方法
})();


(() => {
  class Animal {
    // 在子类和外部都不可见, 只能在当前类内部可见
    private name: string = 'tom'
    // 在子类中也可见
    protected age: number = 3 

    // 默认在子类和外部都可见
    public sayInfo () {
      console.log(this.name, this.age)
    }
  }

  class Dog extends Animal {
    sayinfo2 () {
      console.log('sayinfo2', this.age/* , this.name */)
    }
  }

  const a = new Animal()
  a.sayInfo()

})();

(() => {
  /* 
静态属性, 是类对象的属性
非静态属性, 是类的实例对象的属性
*/

  class Person {
    name1: string = 'A'
    static name2: string = 'B'
  }

  console.log(Person.name2)
  console.log(new Person().name1)

})();

(() => {
  /*
  多态
  */
  class Animal {
    name: string

    constructor (name: string) {
      this.name = name
    }

    run (distance: number=0) {
      console.log(`${this.name} run ${distance}m`)
    }
  }

  class Snake extends Animal {
    constructor (name: string) {
      // 调用父类型构造方法
      super(name)
    }

    // 重写父类型的方法
    run (distance: number=5) {
      console.log('sliding...')
      super.run(distance)
    }
  }

  class Horse extends Animal {
    constructor (name: string) {
      // 调用父类型构造方法
      super(name)
    }

    // 重写父类型的方法
    run (distance: number=50) {
      console.log('dashing...')
      // 调用父类型的一般方法
      super.run(distance)
    }
  }

  /* 
    多态的前提:
      1. 继承
      2. 方法重写
    在编码时, 
      声明接收的是一个父类型对象, 并调用父类型的方法
      但实际传入的可能是任意子类型的对象, 那最终执行是某个子类型对象的方法
  */

  // 声明接收父类型对象
  // 多态: 多种形态 => 声明需要的是一个Animal类型的对象, 
  // 实际可以传入animal/horse/snake, 最终调用的都是实际对象的方法
  function run (animal: Animal) {
    animal.run()
  }

  run(new Animal('aa')) // 调用animal中的run  0m
  run(new Snake('ss')) // 调用子类对象snake的run 5m
  run(new Horse('hh')) // 调用子类对象horse的run 50m
})();